Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

@ammar-agent ammar-agent commented Oct 17, 2025

Problem

Consumer breakdown can get stuck in "Calculating..." state forever when the Web Worker hangs and never resolves/rejects its promise.

Root cause: No timeout on tokenWorker.calculate() means if the worker hangs:

  • Promise never settles
  • Code never reaches catch/finally blocks
  • pendingCalcs never cleaned up
  • UI stuck showing "Calculating..." indefinitely

This was observed during development when errors occurred.

Solution

Add a 10-second timeout using Promise.race:

const timeoutPromise = new Promise<never>((_, reject) =>
  setTimeout(() => reject(new Error("Calculation timeout")), 10_000)
);

const fullStats = await Promise.race([
  this.tokenWorker.calculate(messages, model),
  timeoutPromise,
]);

Why 10 seconds?

  • Generous enough for large message histories
  • Responsive - anything longer feels like a bug to users
  • Treated as error → caches empty result → prevents retry spam

Unchanged: Original Cancellation Logic

The existing cancellation handling is preserved and correct:

catch (error) {
  if (error.message === "Cancelled by newer request") {
    return; // Don't cache, let lazy trigger retry
  }
  // Real errors: cache empty to prevent infinite retries
  this.cache.set(workspaceId, { ..., isCalculating: false });
}

Why this matters:

  • Cancellations (transient): Don't cache → Lazy trigger retries naturally
  • Real errors (permanent): Cache empty → Prevent retry spam
  • Removing this distinction breaks the system

Net Changes

  • Added: 6 lines (timeout constant + Promise.race wrapper)
  • Removed: 0 lines
  • Net: +6 lines, 0 complexity increase

Testing

  • ✅ Fresh load with large history (timeout doesn't trigger prematurely)
  • ✅ Rapid workspace switching (cancellations still work correctly)
  • ✅ Simulated worker hang (timeout fires, UI exits calculating state)

Generated with cmux

@ammar-agent ammar-agent force-pushed the fix-consumer-initial-load branch from 2517763 to 742245b Compare October 17, 2025 00:09
When the Web Worker hangs and never resolves/rejects the promise,
the calculation gets stuck in 'Calculating...' state forever because
pendingCalcs is never cleaned up.

Add Promise.race with a 10-second timeout to guarantee the promise
always settles. 10s is generous for large histories while remaining
responsive (30s+ feels like a bug to users).

Timeout is treated as an error (caches empty result to prevent retry spam).

Original cancellation handling is preserved - cancellations don't cache
so lazy trigger can naturally retry on next access.

Net: +6 lines, 0 complexity increase
@ammar-agent ammar-agent force-pushed the fix-consumer-initial-load branch from 742245b to 72998c6 Compare October 17, 2025 00:14
@ammario ammario enabled auto-merge October 17, 2025 00:17
@ammario ammario added this pull request to the merge queue Oct 17, 2025
Merged via the queue into main with commit b2b3999 Oct 17, 2025
8 checks passed
@ammario ammario deleted the fix-consumer-initial-load branch October 17, 2025 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants